#include "gameview.h"

#include <QPainter>
#include <QDebug>
#include <QMouseEvent>
#include <QImage>

GameView::GameView(Game *game, QWidget *parent) : QWidget(parent)
{
    assert(game != nullptr);
    this->game = game;

    setMinimumSize(QSize(200, 200));
    calculateGridSize();
    grabKeyboard();
}

void GameView::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);

    QPainter p(this);

    // draw background
    p.fillRect(0, 0, width(), height(), QColor(250, 250, 250));

    // draw grids
    p.setPen(QColor(120, 120, 120));
    for (int i = 1; i < game->mapSize.width(); ++i) {
        qreal x = i * gridSize.width();
        QPointF from(x, 0), to(x, height());
        p.drawLine(from, to);
    }
    for (int i = 1; i < game->mapSize.height(); ++i) {
        qreal y = i * gridSize.height();
        QPointF from(0, y), to(width(), y);
        p.drawLine(from, to);
    }

    // snake head
    p.fillRect(getCoordRect(game->snake.front()), QColor(220, 20, 20));

    // snake body
    for (size_t i = 1; i < game->snake.size(); ++i)
        p.fillRect(getCoordRect(game->snake[i]), QColor(80, 80, 80));

    // obstacles
    for (const auto& pt : game->obstacles)
        p.fillRect(getCoordRect(pt), QGradient::HappyMemories);

    // food
    // p.fillRect(getCoordRect(game->food), QGradient::LoveKiss);
    p.drawImage(getCoordRect(game->food), QImage(":/icon/fruit.png"));

    auto drawText = [this, &p](const QString& text) {
        int fontSize = width() * 48.0 / 500;
        if (fontSize < 12)
            fontSize = 12;
        QFont font("Arial", fontSize, QFont::Bold);
        QRect rect(0, 0, width(), height());
        p.setFont(font);
        p.drawText(rect, Qt::AlignCenter, text);
    };

    if (game->state == Game::State::over) {
        drawText("Game Over!");
    } else if (game->state == Game::State::paused) {
        drawText("PAUSED");
    }
}

void GameView::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event);

    calculateGridSize();

    qDebug() << "GameView: " << size();
}

void GameView::mousePressEvent(QMouseEvent *event)
{
    if (game->state == Game::State::initial) {
        int i = event->x() / gridSize.width();
        int j = event->y() / gridSize.height();

        // obstacle cannot be placed on food / snake
        QPoint pos(i, j);
        if (pos == game->food)
            return;
        if (game->collideWithSnake(pos))
            return;

        if (game->hasObstacleAt(i, j)) {
            game->removeObstacleAt(i, j);
        } else {
            game->addObstacleAt(i, j);
        }

        update();
    }
}

void GameView::keyPressEvent(QKeyEvent *event)
{
    bool dirKeyPressed = false;
    bool spacePressed = false;
    Direction dir;

    switch (event->key()) {
    case Qt::Key_Up:
        qDebug() << "up";
        dir = Direction::up;
        dirKeyPressed = true;
        break;
    case Qt::Key_Down:
        qDebug() << "down";
        dir = Direction::down;
        dirKeyPressed = true;
        break;
    case Qt::Key_Left:
        qDebug() << "left";
        dir = Direction::left;
        dirKeyPressed = true;
        break;
    case Qt::Key_Right:
        qDebug() << "right";
        dir = Direction::right;
        dirKeyPressed = true;
        break;
    case Qt::Key_Space:
        spacePressed = true;
        break;
    }

    if (dirKeyPressed) {
        if (game->state == Game::State::ready ||
            game->state == Game::State::started)
            game->setSnakeDirection(dir);
        if (game->state == Game::State::ready &&
            dir != oppositeDirection(game->dir))
            game->start();
    } else if (spacePressed) {
        if (game->state == Game::State::started)
            game->pause();
        else if (game->state == Game::State::paused)
            game->start(); // resume
        else if (game->state == Game::State::initial)
            game->ready();
    }
}

void GameView::onGameUpdate()
{
    update();
}

void GameView::onGameStateChanged(Game::State prev, Game::State next)
{
    Q_UNUSED(prev);
    Q_UNUSED(next);

    update();
}

void GameView::calculateGridSize()
{
    qreal vw = width(), vh = height();
    gridSize.setWidth(vw / game->mapSize.width());
    gridSize.setHeight(vh / game->mapSize.height());
}

QRectF GameView::getCoordRect(int i, int j)
{
    qreal x = gridSize.width() * i;
    qreal y = gridSize.height() * j;
    return QRectF(x, y, gridSize.width(), gridSize.height());
}

QRectF GameView::getCoordRect(const QPoint& p)
{
    return getCoordRect(p.x(), p.y());
}
